package com.wutong.engine.entity.animation;

import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.component.Component;
import com.almasb.fxgl.texture.AnimatedTexture;
import com.almasb.fxgl.texture.AnimationChannel;
import com.almasb.fxgl.texture.Texture;
import com.wutong.engine.anything.Config;
import com.wutong.engine.anything.GameLogger;
import com.wutong.engine.entity.GameEntity;
import javafx.beans.property.BooleanProperty;
import javafx.util.Duration;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 常用类型动画压缩
 *
 * @author 王益凡
 */
public class Animation extends Component {
    private static int animationId = 1000000;
    private static final Logger log = GameLogger.getLogger(Animation.class);

    private final int id;
    private String animationName;
    private final Map<String, AnimationChannel> channelMap = new HashMap<>();
    private final Map<String, AnimatedTexture> textureMap = new HashMap<>();

    private final ArrayList<String> channelNames = new ArrayList<>();

    private final Map<String, String> animationFilePathMap = new HashMap<>();
    private final Map<String, Integer> framesMap = new HashMap<>();
    private final Map<String, Integer[]> sizeMap = new HashMap<>();
    private final Map<String, Duration> durationMap = new HashMap<>();
    private final Map<String, Integer[]> whereMap = new HashMap<>();

    private final GameEntity thisEntity;
    private String defaultChannel;

    private static AnimatedTexture nowTexture;

    private static final double TPF;

    static {
        Map<String, Object> map = Config.loadJsonToMap("NormalSetting");
        double fps = (double) map.get("fps");
        TPF = 1 / fps;
        log.debug("已固定tpf: {}", TPF);
        log.debug("已固定fps: {}", fps);
    }

    /**
     * 构造函数
     */
    public Animation(String animationName, GameEntity gameEntity) {
        this.id = animationId++;
        this.animationName = animationName;
        thisEntity = gameEntity;
    }

    /**
     * 装载动画通道，以便加载
     */
    public void loadAnimationChannel() {
        channelNames.forEach(channelName -> {
            log.info("装载 {} 动画", channelName);
            channelMap.put(channelName, new AnimationChannel(
                    FXGL.image(animationFilePathMap.get(channelName)),
                    framesMap.get(channelName),
                    sizeMap.get(channelName)[0],
                    sizeMap.get(channelName)[1],
                    durationMap.get(channelName),
                    whereMap.get(channelName)[0],
                    whereMap.get(channelName)[1]
            ));
            textureMap.put(channelName, new AnimatedTexture(channelMap.get(channelName)));
            log.info("装载 {} 动画完毕", channelName);
        });
    }

    /**
     * 添加动画频道以用于载入动画
     *
     * @param channelName       动画名 -> 该动画的名字
     * @param animationFilePath 动画所在帧动画序列图片所在位置，从assets/textures下开始索引
     * @param frames            一共帧数
     * @param from              从第几帧开始 0 索引开始
     * @param to                到第几帧结束 0 索引开始
     * @param width             帧动画宽度
     * @param height            帧动画高度
     * @param duration          完成一次帧动画需要的时间
     */
    public void addAnimationChannel(String channelName, String animationFilePath, int frames, int from, int to, int width, int height, Duration duration) {
        log.info("添加 {} 动画", channelName);
        channelNames.add(channelName);
        animationFilePathMap.put(channelName, animationFilePath);
        framesMap.put(channelName, frames);
        sizeMap.put(channelName, new Integer[]{width, height});
        durationMap.put(channelName, duration);
        whereMap.put(channelName, new Integer[]{from, to});
        log.info("添加 {} 动画完毕", channelName);
    }

    public void setDefaultAnimation(String channelName) {
        defaultChannel = channelName;
    }

    public void switchAnimation(AnimationChannel nextAnimation) {
        // log.debug("切换动画");
        if (Optional.ofNullable(nowTexture).isPresent()) {
            nowTexture.loopAnimationChannel(nextAnimation);
        } else {
            log.warn("切换失败，正在重新加载动画");
            nowTexture = new AnimatedTexture(nextAnimation);
            nowTexture.loopAnimationChannel(nextAnimation);
        }
    }

    public void switchAnimation(String channelName) {
        switchAnimation(channelMap.get(channelName));
    }

    /**
     * 返回一个 BooleanProperty 对象，表示动画是否暂停。
     * <br>
     * 可以通过绑定此属性来控制动画的播放和暂停。
     * <br>
     * 例如：animation.pausedProperty().bind(someCondition)
     *
     * @return pausedProperty 属性
     */
    @Override
    public BooleanProperty pausedProperty() {
        return super.pausedProperty();
    }

    /**
     * 当此动画组件被添加到游戏实体 (Entity) 时，FXGL 引擎会自动调用此方法。
     * <br>
     * 可以在此方法中执行初始化操作，例如加载动画资源。
     * <br>
     * 加载完以后自动执行默认动画的loop
     */
    @Override
    public void onAdded() {
        log.debug("调用onAdded方法");
        if (defaultChannel == null) {
            String tmp = textureMap.keySet().toArray(String[]::new)[0];
            log.warn("未知的defaultChannel，将默认使用 {}", tmp);
            defaultChannel = tmp;
        }
        log.info("{} 类设置默认动画显示通道 {}", animationName, defaultChannel);
        nowTexture = textureMap.get(defaultChannel);
        entity.getViewComponent().addChild(nowTexture);
        nowTexture.loopAnimationChannel(channelMap.get(defaultChannel));
    }

    /**
     * 这是动画逻辑的核心方法，每帧都会被调用。
     * <br>
     * 您需要在此方法中更新动画状态，例如更改精灵帧、移动对象位置等。
     *
     * @param tpf (time per frame) 表示两帧之间的时间间隔，用于确保动画速度与帧率无关。<br>由FPS转换为：FPS = 1 / TPF, TPF = 1 / FPS
     */
    @Override
    public void onUpdate(double tpf) {
        tpf = TPF;
    }

    /**
     * 当此动画组件从游戏实体中移除时调用。
     * <br>
     * 可以在此方法中执行清理操作，例如释放动画资源。
     */
    @Override
    public void onRemoved() {
        super.onRemoved();
    }

    /**
     * 指示此组件是否需要依赖注入。如果您的组件依赖于其他组件或服务，则应该返回 true
     *
     * @return 如果需要组件注入，则返回 true；否则返回 false
     */
    @Override
    public boolean isComponentInjectionRequired() {
        return super.isComponentInjectionRequired();
    }

    /**
     * 返回此动画组件的字符串表示形式，用于调试和日志记录。
     *
     * @return 此组件的字符串表示形式
     */
    @Override
    public String toString() {
        return super.toString();
    }

    public int getId() {
        return id;
    }

    public String getAnimationName() {
        return animationName;
    }

    public void setAnimationName(String animationName) {
        this.animationName = animationName;
    }

    public GameEntity getThisEntity() {
        return thisEntity;
    }
}
